package com.qianfeng.libra.cowshop.app.utils;

import android.content.Context;
import android.os.Environment;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Created by Libra丶ZW on 2015/2/6 0006.
 */
public class FileCache {
    private static FileCache ourInstance;

    /**
     * 这个方法必须在 getInstance之前调用，主要用于初始化Android中特有的Context和应用程序
     * 私有缓存目录的功能。
     *
     * @param context
     * @return
     */
    public static FileCache createInstance(Context context) {
        if (ourInstance == null) {
            ourInstance = new FileCache(context);
        }
        return ourInstance;
    }

    public static FileCache getInstance() {
        if (ourInstance == null) {
            throw new IllegalStateException("FileCache must invoke createInstance(Context) before getInstance()");
        }
        return ourInstance;
    }

    // Android上下文Context
    private Context context;

    /**
     * 当前文件缓存的目录，主要针对 网址上的资源，下载到本地的情况
     */
    private File fileCacheDir;

    /**
     * 在私有构造方法中指定参数，那么这个构造方法必须由当前类
     * 进行初始化，那么必须在静态方法中调用。
     */
    private FileCache(Context context) {
        this.context = context;

        if (context != null) {
            // 上下文有，那么进行存储设置
            String state = Environment.getExternalStorageState();

            if (Environment.MEDIA_MOUNTED.equals(state)) {
                // SD卡存在
                fileCacheDir = context.getExternalCacheDir();
            } else {
                fileCacheDir = context.getCacheDir();
            }
        } else {
            // 上下文没有，只能自己用SD卡存储，一个自定义的目录离了

            String state = Environment.getExternalStorageState();

            if (Environment.MEDIA_MOUNTED.equals(state)) {
                File directory = Environment.getExternalStorageDirectory();
                fileCacheDir = new File(directory, ".youshare");
            } else {
                throw new IllegalArgumentException("FileCache must set context or device has a sdcard.");
            }
        }

        if (fileCacheDir != null) {
            if (!fileCacheDir.exists()) {
                fileCacheDir.mkdirs();
            }
        } else {
            throw new IllegalArgumentException("FileCache can't get a cache dir.");
        }
    }

    /**
     * 将下载的内容根据 url映射规则，保存到相应的文件中
     * @param url
     * @param data
     */
    public void putContent(String url, byte[] data){
        if(url != null && data != null){
            // url -> 文件名  映射

            String fileName = mapUrlToFile(url);

            File targetFile = new File(fileCacheDir, fileName);

            // 如果文件不存在，必须通过 createNewFile 进行调用创建后才可以写文件

            boolean bok = true;
            if(!targetFile.exists()){
                try {
                    bok = targetFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // bok 代表文件是否存在

            if(bok){
                FileOutputStream fout = null;

                try {
                    fout = new FileOutputStream(targetFile);
                    fout.write(data);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    StreamUtil.close(fout);
                }
            }

        }
    }

    /**
     * 获取url代表的数据，从本地存储中，如果有本地文件，那么返回有效，否则返回 null
     *
     * @param url
     * @return
     */
    public byte[] getContent(String url) {
        byte[] ret = null;

        if (url != null) {

            // TODO URL 中通常包含了不能够作为文件名的一些字符，这种情况要进行映射
            // 讲 URL 转变为 唯一的，可以作为文件名存在的字符串。
            // 接触一个叫做 MD5 的消息摘要算法。

            String fileName = mapUrlToFile(url);
            if (fileName.length() > 0) {

                File targetFile = new File(fileCacheDir, fileName);

                if (targetFile.exists()) {
                    // TODO 读取文件到字节数组

                    FileInputStream fin = null;

                    try {
                        fin = new FileInputStream(targetFile);
                        ret = StreamUtil.readStream(fin);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        StreamUtil.close(fin);
                        fin = null;
                    }

                }
            }
        }

        return ret;
    }

    /**
     * 将 url 转变为 文件名
     *
     * @param url 网址
     * @return String 文件名
     */
    private static String mapUrlToFile(String url) {
        String ret = null;
        if (url != null) {
            // TODO 将 url 进行 MD5 消息摘要
            // MD5 将任何一个内容转变为 不可逆的，唯一的 一个字节数组

            byte[] md5 = md5(url.getBytes());

            ret = hex(md5);

        }
        return ret;
    }

    /**
     * MD5算法将输入的字节数组转换为唯一的一个字节数组信息。
     *
     * @param data
     * @return
     */
    private static byte[] md5(byte[] data) {
        byte[] ret = null;
        if (data != null && data.length > 0) {
            try {
                MessageDigest digest = MessageDigest.getInstance("MD5");
                ret = digest.digest(data); // 将 data 进行消息摘要，生成一个特定的字节数组，当中的数值不可逆转
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }
        return ret;
    }

    /**
     * 将字节数组中的每一个字节转换为0-9 a-f 这些可以显示的十六进制表示法的字符
     *
     * @param data
     * @return
     */
    private static String hex(byte[] data) {
        StringBuilder sb = new StringBuilder();

        if (data != null && data.length > 0) {
            for (byte b : data) {

                int h, l;

                h = (b >> 4) & 0x0f;
                l = b & 0x0f;

                sb.append(Integer.toHexString(h));
                sb.append(Integer.toHexString(l));
            }
        }

        return sb.toString();
    }


}